/*
 *  Copyright 2008 the original author or authors.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package app.layouts;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.noos.xing.mydoggy.AggregationPosition;
import org.noos.xing.mydoggy.Content;
import org.noos.xing.mydoggy.ContentManager;
import org.noos.xing.mydoggy.ContentManagerUIListener;
import org.noos.xing.mydoggy.MultiSplitConstraint;
import org.noos.xing.mydoggy.MultiSplitContentManagerUI;
import org.noos.xing.mydoggy.PersistenceDelegate.MergePolicy;
import org.noos.xing.mydoggy.PersistenceDelegateCallback;
import org.noos.xing.mydoggy.TabbedContentManagerUI;
import org.noos.xing.mydoggy.ToolWindowManager;
import org.noos.xing.mydoggy.event.ContentManagerUIEvent;
import org.noos.xing.mydoggy.plaf.MyDoggyToolWindowManager;
import org.noos.xing.mydoggy.plaf.ui.cmp.ExtendedTableLayout;
import org.noos.xing.mydoggy.plaf.ui.content.MyDoggyMultiSplitContentManagerUI;
import org.springframework.richclient.application.ApplicationWindow;
import org.springframework.richclient.application.PageComponent;
import org.springframework.richclient.application.PageDescriptor;
import org.springframework.richclient.application.PageLayoutBuilder;
import org.springframework.richclient.application.setup.SetupWizard;
import org.springframework.richclient.application.support.AbstractApplicationPage;
import org.springframework.richclient.util.Assert;

/**
 * This page represents one page for the spring rich client with
 * the mydoggy framework. In pure Swing apps one would use a JFrame instead.
 * 
 * TODO 
 * # remove a view => NPE exception
 * # load layout at startup! (TODO layout.xml should exist!)
 * 
 * @author Peter Karich, peat_hal ‘at’ users ‘dot’ sourceforge ‘dot’ net
 */
public class MyDoggyApplicationPage extends AbstractApplicationPage implements PageLayoutBuilder {

    private JPanel rootComponent;
    private MyDoggyToolWindowManager toolWindowManager;
    private ContentManager contentManager;
    private MultiSplitContentManagerUI contentManagerUI;
    private Map<String, Entry<Content, PageComponent>> contentAndPageComponentById;

    MyDoggyApplicationPage(ApplicationWindow window, PageDescriptor pageDescriptor) {
        super(window, pageDescriptor);
        contentAndPageComponentById = new HashMap<String, Entry<Content, PageComponent>>();
        toolWindowManager = new MyDoggyToolWindowManager();
        contentManager = toolWindowManager.getContentManager();
        contentManagerUI = new MyDoggyMultiSplitContentManagerUI();
        contentManager.setContentManagerUI(contentManagerUI);

        assert SwingUtilities.isEventDispatchThread();

        contentManagerUI.setTabPlacement(TabbedContentManagerUI.TabPlacement.TOP);
        contentManagerUI.setShowAlwaysTab(true);
        contentManagerUI.setCloseable(true);
        contentManagerUI.setDetachable(true);
        contentManagerUI.setMinimizable(true);

        contentManagerUI.addContentManagerUIListener(new MyDoggyContentListener());

    //TODO
    //TabbedContentUI contentUI = contentManagerUI.getContentUI(toolWindowManager.getContentManager().getContent(0));
    //contentUI.setTransparentMode(true);
    //contentUI.setTransparentDelay(1000);
    //contentUI.setTransparentRatio(0.7f);
    }

    @Override
    protected void doAddPageComponent(PageComponent pageComponent) {
        Assert.notNull(pageComponent);

        // If we have at least one content, then dock into that one!
        MultiSplitConstraint constraint;
        if (contentAndPageComponentById.values().iterator().hasNext()) {
            constraint = new MultiSplitConstraint(contentAndPageComponentById.values().iterator().next().getKey());
        } else {
            constraint = new MultiSplitConstraint(AggregationPosition.DEFAULT);
        }

        // Create and register a new content to the mydoggy layout manager
        Content c = contentManager.addContent(
                pageComponent.getId(),
                pageComponent.getDisplayName(), // tabName
                pageComponent.getIcon(),
                pageComponent.getControl(),
                pageComponent.getDisplayName(), // toolTip
                constraint);

        Assert.notNull(c);
        Assert.isTrue(c.getId().equals(pageComponent.getId()));
        contentAndPageComponentById.put(pageComponent.getId(),
                new DoggyEntry<Content, PageComponent>(c, pageComponent));

        // trigger the createControl method of the PageComponent, so if a
        // PageComponentListener is added
        // in the createControl method, the componentOpened event is received.
        pageComponent.getControl();
    }

    @Override
    protected void doRemovePageComponent(PageComponent pageComponent) {
        Entry<Content, PageComponent> e = contentAndPageComponentById.remove(pageComponent.getId());
        Assert.notNull(e);
        boolean ret = contentManager.removeContent(e.getKey());
        Assert.isTrue(ret);
    }

    @Override
    protected boolean giveFocusTo(PageComponent pageComponent) {
        System.out.println("Foco passou para essa pagina_--------------------------------------");
        return pageComponent.getControl().requestFocusInWindow();
    }

    @Override
    public void setActiveComponent(PageComponent pageComponent) {
        if (pageComponent != null) {
            // really necessary?
            Content c = getContent(pageComponent.getId());
            if (c != null) {
                c.ensureVisible();
            }
            setActive(pageComponent.getId());
        }
        super.setActiveComponent(pageComponent);
    }

    @Override
    protected JComponent createControl() {
        // At startup create the main component and set the mydoggy specific
        // layout manager.       
        Assert.isNull(rootComponent, "Should not being initialized twice.");
        rootComponent = new JPanel();
        rootComponent.setLayout(new ExtendedTableLayout(new double[][]{{0, -1, 0}, {0, -1, 0}}));
        rootComponent.add(toolWindowManager, "1,1,");

        return rootComponent;
    }

    public void addView(String viewDescriptorId) {
        // called from pageDescriptor.buildInitialLayout (then pageBuilder.addView)
        showView(viewDescriptorId);
    }

    /* Now some helper methods and classes */
    private Content getContent(String id) {
        return contentManager.getContent(id);
    }

    private PageComponent getPageComponent(String id) {
        Entry<Content, PageComponent> entry =
                contentAndPageComponentById.get(id);
        if (entry == null) {
            return null;
        }

        return entry.getValue();
    }

    void buildInitialLayout() {
        getPageDescriptor().buildInitialLayout(this);
    }

    /**
     * This method displays the component with specified key
     * @return true if it was successful.
     */
    private boolean setActive(final String id) {
        Assert.isTrue(SwingUtilities.isEventDispatchThread());

        Content c = getContent(id);
        if (c != null) {
            c.setSelected(true);
            return true;
        }
        return false;
    }

    @Override
    public boolean close() {
        try {
            saveLayout();
        } catch (IOException ex) {
            logger.warn("IO Error while saving layout!", ex);
        }
        return super.close();
    }

    boolean loadLayout() throws IOException {
        File file = getLayoutFile();
        if (file != null && file.canRead()) {
            Assert.isTrue(SwingUtilities.isEventDispatchThread());
            Assert.isTrue(rootComponent.isVisible());

            FileInputStream inputStream = new FileInputStream(file);

            // load layout, but add the requested content before (via callback)!
            toolWindowManager.getPersistenceDelegate().merge(
                    inputStream, MergePolicy.UNION,
                    new PersistenceDelegateCallback() {

                        public Content contentNotFound(ToolWindowManager toolWindow, String id) {
                            addView(id);
                            return contentAndPageComponentById.get(id).getKey();
                        }
                    });
            inputStream.close();

            //logger.info("Loaded layout from file " + file);
            logger.info("Loaded " + contentManager.getContentCount() + " view(s).");
            return true;
        }
        return false;
    }

    void saveLayout() throws IOException {
        File file = getLayoutFile();
        if (file != null && file.canWrite()) {
            FileOutputStream output = new FileOutputStream(file);
            toolWindowManager.getPersistenceDelegate().save(output);

            output.close();
            //logger.info("Successfully saved layout.");
            logger.info("Saved " + contentManager.getContentCount() + " view(s).");
        }
    }

    private File getLayoutFile() throws IOException {
        PageDescriptor pageDescriptor = getPageDescriptor();

        if (pageDescriptor instanceof MyDoggyPageDescriptor) {
            return ((MyDoggyPageDescriptor) pageDescriptor).getInitialLayout().getFile();
        } else {
            throw new IOException("Couldn't find layout file.");
        }
    }

    /**
     *  SpringRC should be informed of a MyDoggy closing event.
     */
    private class MyDoggyContentListener implements ContentManagerUIListener {

        public boolean contentUIRemoving(ContentManagerUIEvent cmEvent) {
            Content content = cmEvent.getContentUI().getContent();
            Assert.notNull(content);
            PageComponent pc = getPageComponent(content.getId());
            Assert.notNull(pc);
            close(pc);
            
            // let mydoggy remove the page ! (don't no for sure if this is the reason of the NPE in mydoggy if we return close(pc))
            return false;
        }

        public void contentUIDetached(ContentManagerUIEvent cmEvent) {
        }
    }

    private static class DoggyEntry<K, V> implements Map.Entry<K, V> {

        private K key;
        private V innerValue;

        DoggyEntry(K key, V value) {
            this.key = key;
            this.innerValue = value;
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return innerValue;
        }

        public V setValue(V value) {
            V old = this.innerValue;
            this.innerValue = value;
            return old;
        }
    }
}
